package com.hframe.filter;

import com.hframe.data.DRef;
import com.hframe.type.*;
import com.hframe.typefilter.FilterRef;
import com.hframe.utils.FileUtil;
import com.hframe.xml.XmlFiletr;
import com.hframe.xml.XmlToData;
import org.apache.log4j.Logger;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * -实现url伪静态化
 * Created by heqian on 17-9-25.
 */
public class UrlRewrite implements Filter {
    private static final Logger log = Logger.getLogger(UrlRewrite.class);
    private static final String listName = "UrlList" + UrlRewrite.class;
    public static final String requestURI = "requestURI" + UrlRewrite.class;

    private UrlList urlList = null;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        urlList = (UrlList) filterConfig.getServletContext().getAttribute(listName);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (null != urlList) {
            String reUrl, url = ((HttpServletRequest) servletRequest).getServletPath();

            TBoolean redirect = new TBoolean(false);
            if (null == (reUrl = getOutboundUrl(url, redirect))) {
                reUrl = getRuleUrl(url, redirect);
            }
            if (null != reUrl) {
                servletRequest.setAttribute(requestURI, ((HttpServletRequest) servletRequest).getRequestURI());

                if (redirect.get()) {
                    ((HttpServletResponse) servletResponse).sendRedirect(reUrl);
                } else {
                    log.debug(url + " To " + reUrl);
                    servletRequest.getRequestDispatcher(reUrl).forward(servletRequest, servletResponse);
                }
                return;
            }
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    private String getRuleUrl(String url, TBoolean redirect) {
        Matcher matcher;
        UrlItem rule = urlList.rule.get();
        if (!rule.from.isEmpty() && (matcher = rule.from.get().matcher(url)).matches()) {
            redirect.set("true".equals(rule.to.filter(XmlFiletr.class).getAttribute("redirect")));
            return replaceUrl(url, matcher, rule);
        }
        return null;
    }

    private String getOutboundUrl(String url, TBoolean redirect) {
        Matcher matcher;
        for (UrlItem rule : urlList.outbound.get()) {
            if (!rule.from.isEmpty() && (matcher = rule.from.get().matcher(url)).matches()) {
                redirect.set("true".equals(rule.to.filter(XmlFiletr.class).getAttribute("redirect")));
                return replaceUrl(url, matcher, rule);
            }
        }
        return null;
    }

    private String replaceUrl(String url, Matcher matcher, UrlItem rule) {
        if (rule.to.isEmpty())
            return null;
        String ret = rule.to.get();

        for (int i = 1; i <= matcher.groupCount(); i++) {
            ret = ret.replace("$" + i, matcher.group(i));
        }
        return ret;
    }

    @Override
    public void destroy() {

    }

    public static String url(HttpServletRequest request, String url, Param... param) {
        request.getServletContext().getAttribute(listName);
        return "";
    }

    public static Param p(String key, Object val) {
        return new Param(key, val);
    }

    private static class Param {
        String key;
        Object val;

        public Param(String key, Object val) {
            this.key = key;
            this.val = val;
        }
    }

    public static boolean init(ServletContext context) {
        String filePath = context.getRealPath("/WEB-INF/UrlRewrite.xml");
        if (new File(filePath).isFile()) {
            try {
                UrlList urlList = new UrlList();
                if (XmlToData.convert(DocumentHelper.parseText(FileUtil.readString(filePath)).getRootElement(), urlList)) {
                    context.setAttribute(listName, urlList);
                    return true;
                }
            } catch (DocumentException e) {
                log.error(e.toString(), e);
            }
        }
        return false;
    }


    public static class UrlList extends DRef<UrlList> {
        public TData<UrlItem> rule = new TData<>(UrlItem.class);
        public TArray<UrlItem> outbound = new TArray<>(UrlItem.class);
    }


    public static class UrlItem extends DRef<UrlItem> {
        public T<Pattern> from = new T<>();
        public TString to = new TString();

        public UrlItem() {
            from.filter(XmlFiletr.class).setSetCall(new FilterRef<Pattern, Object>() {
                @Override
                public Pattern filter(Object object) throws Exception {
                    if (null != object) {
                        try {
                            return Pattern.compile((String) object);
                        } catch (Exception e) {
                            log.error(e.toString(), e);
                        }
                    }
                    return null;
                }
            });
        }
    }
}
